home *** CD-ROM | disk | FTP | other *** search
/ ShareWare OnLine 2 / ShareWare OnLine Volume 2 (CMS Software)(1993).iso / prog / exec33.zip / EXEC.PAS < prev    next >
Pascal/Delphi Source File  |  1991-11-21  |  36KB  |  1,169 lines

  1. Unit exec;
  2. {  --- Version 3.3 91-11-21 16:12 ---
  3.  
  4.    EXEC.PAS: EXEC function with memory swap - prepare parameters.
  5.  
  6.    Needs Assembler file 'spawn.asm' (assembled as 'spawnp.obj')
  7.    and unit 'checkpat'.
  8.  
  9. Public domain software by
  10.  
  11.         Thomas Wagner
  12.         Ferrari electronic GmbH
  13.         Beusselstrasse 27
  14.         D-1000 Berlin 21
  15.         West Germany
  16.  
  17.         BIXname: twagner
  18. }
  19.  
  20. Interface
  21.  
  22. Uses
  23.   Dos, checkpat;
  24.  
  25. const
  26.  
  27. {e Return codes (only upper byte significant) }
  28. {d Fehlercodes (nur das obere Byte signifikant) }
  29.  
  30.    RC_PREPERR   = $0100;
  31.    RC_NOFILE    = $0200;
  32.    RC_EXECERR   = $0300;
  33.    RC_ENVERR    = $0400;
  34.    RC_SWAPERR   = $0500;
  35.    RC_REDIRERR  = $0600;
  36.  
  37. {e Swap method and option flags }
  38. {d Auslagerungsmethoden ond Optionen }
  39.  
  40.    USE_EMS      =  $01;
  41.    USE_XMS      =  $02;
  42.    USE_FILE     =  $04;
  43.    EMS_FIRST    =  $00;
  44.    XMS_FIRST    =  $10;
  45.    HIDE_FILE    =  $40;
  46.    NO_PREALLOC  = $100;
  47.    CHECK_NET    = $200;
  48.  
  49.    USE_ALL      = USE_EMS or USE_XMS or USE_FILE or CHECK_NET;
  50.  
  51.  
  52. type
  53.     filename = string [81];
  54.     string128 = string [128];
  55.     pstring = ^string;
  56.  
  57.  
  58. function do_exec (xfn: string; pars: string; spawn: integer;
  59.                   needed: word; newenv: boolean): integer;
  60.  
  61.    {>e
  62.       The EXEC function.
  63.  
  64.       Parameters:
  65.  
  66.          xfn      is a string containing the name of the file
  67.                   to be executed. If the string is empty,
  68.                   the COMSPEC environment variable is used to
  69.                   load a copy of COMMAND.COM or its equivalent.
  70.                   If the filename does not include a path, the
  71.                   current PATH is searched after the default.
  72.                   If the filename does not include an extension,
  73.                   the path is scanned for a COM, EXE, or BAT file 
  74.                   in that order.
  75.  
  76.          pars     The program parameters.
  77.  
  78.          spawn    If 0, the function will terminate after the 
  79.                   EXECed program returns, the function will not return.
  80.  
  81.                   NOTE: If the program file is not found, the function
  82.                         will always return with the appropriate error 
  83.                         code, even if 'spawn' is 0.
  84.  
  85.                   If non-0, the function will return after executing the
  86.                   program. If necessary (see the "needed" parameter),
  87.                   memory will be swapped out before executing the program.
  88.                   For swapping, spawn must contain a combination of the
  89.                   following flags:
  90.  
  91.                      USE_EMS  ($01)  - allow EMS swap
  92.                      USE_XMS  ($02)  - allow XMS swap
  93.                      USE_FILE ($04)  - allow File swap
  94.  
  95.                   The order of trying the different swap methods can be
  96.                   controlled with one of the flags
  97.  
  98.                      EMS_FIRST ($00) - EMS, XMS, File (default)
  99.                      XMS_FIRST ($10) - XMS, EMS, File
  100.  
  101.                   If swapping is to File, the attribute of the swap file
  102.                   can be set to "hidden", so users are not irritated by
  103.                   strange files appearing out of nowhere with the flag
  104.  
  105.                      HIDE_FILE ($40) - create swap file as hidden
  106.  
  107.                   and the behaviour on Network drives can be changed with
  108.  
  109.                      NO_PREALLOC (0x100) - don't preallocate
  110.                      CHECK_NET (0x200)   - don't preallocate if file on net.
  111.  
  112.                   This checking for Network is mainly to compensate for
  113.                   a strange slowdown on Novell networks when preallocating
  114.                   a file. You can either set NO_PREALLOC to avoid allocation
  115.                   in any case, or let the prep_swap routine decide whether
  116.                   to do preallocation or not depending on the file being
  117.                   on a network drive (this will only work with DOS 3.1 or 
  118.                   later).
  119.  
  120.          needed   The memory needed for the program in paragraphs (16 Bytes).
  121.                   If not enough memory is free, the program will
  122.                   be swapped out.
  123.                   Use 0 to never swap, $ffff to always swap. 
  124.                   If 'spawn' is 0, this parameter is irrelevant.
  125.  
  126.          newenv   If this parameter is FALSE, the environment
  127.                   of the spawned program is a copy of the parent's
  128.                   environment. If it is TRUE, a new environment
  129.                   is created which includes the modifications from
  130.                   previous 'putenv' calls.
  131.  
  132.       Return value:
  133.  
  134.          $0000..00FF: The EXECed Program's return code
  135.  
  136.          $0101:       Error preparing for swap: no space for swapping
  137.          $0102:       Error preparing for swap: program too low in memory
  138.  
  139.          $0200:       Program file not found
  140.          $0201:       Program file: Invalid drive
  141.          $0202:       Program file: Invalid path
  142.          $0203:       Program file: Invalid name
  143.          $0204:       Program file: Invalid drive letter
  144.          $0205:       Program file: Path too long
  145.          $0206:       Program file: Drive not ready
  146.          $0207:       Batchfile/COMMAND: COMMAND.COM not found
  147.          $0208:       Error allocating temporary buffer
  148.  
  149.          $03xx:       DOS-error-code xx calling EXEC
  150.  
  151.          $0400:       Error allocating environment buffer
  152.  
  153.          $0500:       Swapping requested, but prep_swap has not 
  154.                        been called or returned an error.
  155.          $0501:       MCBs don't match expected setup
  156.          $0502:       Error while swapping out
  157.  
  158.          $0600:       Redirection syntax error
  159.          $06xx:       DOS error xx on redirection
  160.    <}
  161.  
  162.    {>d
  163.       Die EXEC Funktion.
  164.  
  165.       Parameter:
  166.  
  167.          xfn      ist ein String mit dem Namen der auszuführenden Datei.
  168.                   Ist der String leer, wird die COMSPEC Umgebungsvariable
  169.                   benutzt um COMMAND.COM oder das Equivalent zu laden.
  170.                   Ist kein Pfad angegeben, wird nach dem aktuellen Pfad
  171.                   der in der PATH Umgebungsvariablen angegebene Pfad
  172.                   durchsucht.
  173.                   Ist kein Dateityp angegeben, wird der Pfad nach
  174.                   einer COM oder EXE Datei (in dieser Reihenfolge) abgesucht.
  175.  
  176.          pars     Die Kommandozeile
  177.  
  178.          spawn    Wenn 0, wird der Programmlauf beendet wenn das
  179.                   aufgerufene Programm zurückkehrt, die Funktion kehrt
  180.                   nicht zurück.
  181.  
  182.                   HINWEIS: Wenn die auszuführende Datei nicht gefunden
  183.                         wird, kehrt die Funktion mit einem Fehlercode
  184.                         zurück, auch wenn der 'spawn' Parameter 0 ist.
  185.  
  186.                   Wenn nicht 0, kehrt die Funktion nach Ausführung des
  187.                   Programms zurück. Falls notwendig (siehe den Parameter
  188.                   "needed") wird der Programmspeicherbereich vor Aufruf
  189.                   ausgelagert.
  190.                   Zur Auslagerung muß der Parameter eine Kombination der
  191.                   folgenden Flags enthalten:
  192.  
  193.                      USE_EMS  ($01)  - Auslagerung auf EMS zulassen
  194.                      USE_XMS  ($02)  - Auslagerung auf XMS zulassen
  195.                      USE_FILE ($04)  - Auslagerung auf Datei zulassen
  196.  
  197.                   Die Reihenfolge der Versuche, auf die verschiedenen
  198.                   Medien auszulagern kann mit einem der folgenden
  199.                   Flags beeinflußt werden:
  200.  
  201.                      EMS_FIRST ($00) - EMS, XMS, Datei (Standard)
  202.                      XMS_FIRST ($10) - XMS, EMS, Datei
  203.  
  204.                   Wenn die Auslagerung auf Datei erfolgt, kann das
  205.                   Attribut dieser Datei auf "hidden" gesetzt werden,
  206.                   damit der Benutzer nicht durch unversehends auftauchende
  207.                   Dateien verwirrt wird:
  208.  
  209.                      HIDE_FILE ($40) - Auslagerungsdatei "hidden" erzeugen
  210.  
  211.                   Außerdem kann das Verhalten auf Netzwerk-Laufwerken 
  212.                   beeinflußt werden mit
  213.  
  214.                      NO_PREALLOC (0x100) - nicht Präallozieren
  215.                      CHECK_NET (0x200)   - nicht Präallozieren wenn Netz.
  216.  
  217.                   Diese Prüfung auf Netzwerk ist hauptsächlich sinnvoll
  218.                   für Novell Netze, bei denen eine Präallozierung eine
  219.                   erhebliche Verzögerung bewirkt. Sie können entweder mit
  220.                   NO_PREALLOC eine Präallozierung in jedem Fall ausschließen,
  221.                   oder die Entscheidung mit CHECK_NET prep_swap überlassen.
  222.                   In diesem Fall wird nicht präalloziert wenn die Datei
  223.                   auf einem Netzwerk-Laufwerk liegt (funktioniert nur
  224.                   mit DOS Version 3.1 und späteren).
  225.  
  226.          needed   Der zur Ausführung des Programms benötigte Speicher
  227.                   in Paragraphen (16 Bytes). Wenn nicht ausreichend 
  228.                   freier Speicher vorhanden ist, wird der Programm-
  229.                   speicherbereich ausgelagert.
  230.                   Bei Angabe von 0 wird nie ausgelagert, bei Angabe
  231.                   von $ffff wird immer ausgelagert.
  232.                   Ist der Parameter 'spawn' 0, hat 'needed' keine Bedeutung.
  233.  
  234.          newenv   Bestimmt die dem gerufenen Programm zu übergebenden 
  235.                   Umgebungsvariablen. Ist der Parameter FALSE,
  236.                   wird eine Kopie der Vater-Umgebung benutzt,
  237.                   d.h. daß Aufrufe von "putenv" keinen Effekt haben.
  238.                   Ist er TRUE, wird eine neue Umgebung mit den 
  239.                   Modifikationen aus 'putenv' übergeben.
  240.  
  241.       Liefert:
  242.  
  243.          $0000..00FF: Rückgabewert des aufgerufenen Programms
  244.  
  245.          $0101:       Fehler bei Vorbereitung zum Auslagern -
  246.                        kein Speicherplatz in XMS/EMS/Datei
  247.          $0102:       Fehler bei Vorbereitung zum Auslagern -
  248.                        der Programmcode ist zu nah am Beginn des
  249.                        Programms.
  250.  
  251.          $0200:       Auszuführende Programmdatei nicht gefunden
  252.          $0201:       Programmdatei: Ungültiges Laufwerk
  253.          $0202:       Programmdatei: Ungültiger Pfad
  254.          $0203:       Programmdatei: Ungültiger Dateiname
  255.          $0204:       Programmdatei: Ungültiger Laufwerksbuchstabe
  256.          $0205:       Programmdatei: Pfad zu lang
  257.          $0206:       Programmdatei: Laufwerk nicht bereit
  258.          $0207:       Batchfile/COMMAND: COMMAND.COM nicht gefunden
  259.          $0208:       Fehler beim allozieren eines temporären Puffers
  260.  
  261.          $03xx:       DOS-Fehler-Code xx bei Aufruf von EXEC
  262.  
  263.          $0400:       Fehler beim allozieren der Umgebungsvariablenkopie
  264.  
  265.          $0500:       Auslagerung angefordert, aber prep_swap wurde nicht
  266.                        aufgerufen oder lieferte einen Fehler
  267.          $0501:       MCBs entsprechen nicht dem erwarteten Aufbau
  268.          $0502:       Fehler beim Auslagern
  269.  
  270.          $0600:      Redirection Syntaxfehler
  271.          $06xx:      DOS-Fehler xx bei Redirection
  272.    <}
  273.  
  274. {>e
  275.    The function pointed to by "spawn_check" will be called immediately 
  276.    before doing the actual swap/exec, provided that
  277.  
  278.       - the preparation code did not detect an error, and
  279.       - "spawn_check" is not NIL.
  280.  
  281.    The function definition is
  282.       function name (cmdbat: integer; swapping: integer; var execfn: string; 
  283.                      var progpars: string): integer;
  284.  
  285.    The parameters passed to this function are
  286.  
  287.       cmdbat      1: Normal EXE/COM file
  288.                   2: Executing BAT file via COMMAND.COM
  289.                   3: Executing COMMAND.COM (or equivalent)
  290.  
  291.       swapping    < 0: Exec, don't swap
  292.                     0: Spawn, don't swap
  293.                   > 0: Spawn, swap
  294.  
  295.       execfn      the file name to execute (complete with path)
  296.  
  297.       progpars    the program parameter string
  298.  
  299.    If the routine returns anything other than 0, the swap/exec will
  300.    not be executed, and do_exec will return with this code.
  301.  
  302.    You can use this function to output messages (for example, the
  303.    usual "enter EXIT to return" message when loading COMMAND.COM)
  304.    and to do clean-up and additional checking.
  305.  
  306.    CAUTION: If swapping is > 0, the routine may not modify the 
  307.    memory layout, i.e. it may not call any memory allocation or
  308.    deallocation routines.
  309.  
  310.    "spawn_check" is initialized to NIL.
  311. <}
  312. {>d
  313.    Die Funktion auf die "spawn_check" zeigt wird unmittelbar vor
  314.    Ausführung des Programmaufrufs aufgerufen, vorausgesetzt daß
  315.  
  316.       - bei der Vorbereitung kein Fehler auftrat, und
  317.       - "spawn_check" nicht NIL ist.
  318.  
  319.    Die Funktionsdefinition ist
  320.       function name (cmdbat: integer; swapping: integer; var execfn: string; 
  321.                      var progpars: string): integer;
  322.  
  323.    Die der Funktion übergebenen Parameter sind
  324.  
  325.       cmdbat      1: Normale EXE/COM Datei
  326.                   2: Ausführung BAT Datei über COMMAND.COM
  327.                   3: Ausführung COMMAND.COM (oder Equivalent)
  328.  
  329.       swapping    < 0: Exec, keine Auslagerung
  330.                     0: Spawn, keine Auslagerung
  331.                   > 0: Spawn, Auslagern
  332.  
  333.       execfn      Name und Pfad der auszuführenden Datei
  334.  
  335.       progpars    Programmparameter
  336.  
  337.    Wenn die Routine einen Wert verschieden von 0 liefert, wird der
  338.    Programmaufruf nicht durchgeführt, und do_exec kehrt mit diesem
  339.    Wert zurück.
  340.  
  341.    Sie können diese Funktion benutzen um Meldungen auszugeben
  342.    (zum Beispiel die übliche Meldung "Geben Sie EXIT ein um 
  343.    zurückzukehren" bei Laden von COMMAND.COM), und für sonstige
  344.    Prüfungen oder Aufräumarbeiten.
  345.  
  346.    ACHTUNG: Wenn swapping > 0 ist, darf die Funktion keinesfalls 
  347.    den Speicheraufbau verändern, d.h. es dürfen keine Speicher-
  348.    Allozierungs oder -Deallozierungsroutinen benutzt werden.
  349.  
  350.    "spawn_check" ist auf NIL initialisiert.
  351. <}
  352.  
  353. type
  354.    spawn_check_proc = function (cmdbat: integer; swapping: integer; 
  355.                                 var execfn: string; var progpars: string)
  356.                                : integer;
  357. var
  358.    spawn_check: spawn_check_proc;
  359.  
  360. {>e
  361.    The 'swap_prep' variable can be accessed from the spawn_check
  362.    call-back routine for additional information on the nature and
  363.    parameters of the swap. This variable will ONLY hold useful
  364.    information if the 'swapping' parameter to spawn_check is > 0.
  365.    The contents of this variable may not be changed.
  366.  
  367.    The 'swapmethod' field will contain one of the flags USE_FILE, 
  368.    USE_XMS, or USE_EMS.
  369.  
  370.    Note that the 'swapfilename' field contains a zero-terminated string
  371.    with no prefixed length byte, not a Pascal string.
  372. <}
  373. {>d
  374.    Die Variable 'swap_prep' kann von der spawn_check Routine
  375.    benutzt werden um zusätzliche Informationen über Art und Parameter
  376.    der Auslagerung zu erfahren. Diese Variable enthält NUR DANN 
  377.    sinnvolle Werte wenn der 'swapping' Parameter von spawn_check > 0 ist.
  378.    Der Inhalt dieser Variablen darf keinesfalls verändert werden.
  379.  
  380.    Das Feld 'swapmethod' enthält einen der Werte USE_FILE, 
  381.    USE_XMS, oder USE_EMS.
  382.  
  383.    Bitte beachten Sie, daß das Feld 'swapfilename' einen Null-terminierten
  384.    String ohne Längenbyte, keinen Pascal-String, enthält.
  385. <}
  386.  
  387. type
  388.    prep_block = record
  389.                   xmm: longint;           {e XMM entry address }
  390.                                           {d Einsprungadresse XMM }
  391.                   first_mcb: integer;     {e Segment of first MCB }
  392.                                           {d Segment des ersten MCB }
  393.                   psp_mcb: integer;       {e Segment of MCB of our PSP }
  394.                                           {d Segment des MCB unseres PSP }
  395.                   env_mcb: integer;       {e MCB of Environment segment }
  396.                                           {d MCB des Umgebungsvariablenblocks }
  397.                   noswap_mcb: integer;    {e MCB that may not be swapped }
  398.                                           {d MCB der nicht Ausgelagert wird }
  399.                   ems_pageframe: integer; {e EMS page frame address }
  400.                                           {d EMS-Seiten-Adresse }
  401.                   handle: integer;        {e EMS/XMS/File handle }
  402.                                           {d Handle für EMS/XMS/Datei }
  403.                   total_mcbs: integer;    {e Total number of MCBs }
  404.                                           {d Gesamtzahl MCBs }
  405.                   swapmethod: byte;       {e Method for swapping }
  406.                                           {d Auslagerungsmethode }
  407.                   swapfilename: array [0..80] of char; 
  408.                                           {e Swap file name if swapping to file }
  409.                                           {d Auslagerungsdateiname }
  410.                   end;
  411.  
  412. var
  413.    swap_prep: prep_block;
  414.  
  415. { ------------------------------------------------------------------------- }
  416.  
  417. procedure putenv (envvar: string);
  418. {  Adds a string to the environment. Note that the change to the
  419.    environment is valid for an exec'ed process only, and only if you
  420.    set the 'newenv' parameter in do_exec to TRUE. }
  421.  
  422.  
  423. function envcount: integer;
  424. function envstr (index: integer): string;
  425. function getenv (envvar: string): string;
  426.  
  427. { Replacement functions for the environment handling functions in the
  428.   DOS unit. All three functions work exactly like their DOS-unit
  429.   counterparts, except that they recognize the changes to the child
  430.   environment produced by 'putenv'. }
  431.  
  432.  
  433.  
  434. {===========================================================================}
  435.  
  436. Implementation
  437.  
  438. {>e
  439.    Define REDIRECT to support redirection.
  440.    CAUTION: The definition in 'spawn.asm' must match this definition!!
  441. <}
  442. {>d
  443.    Definieren Sie REDIRECT um Dateiumleitung zu untertützen.
  444.    ACHTUNG: Die Definition in 'spawn.asm' muß mit dieser Definition 
  445.    übereinstimmen!!
  446. <}
  447.  
  448. {$DEFINE REDIRECT}
  449.  
  450. const
  451.    swap_filename = '$$AAAAAA.AAA';
  452.  
  453.    {e internal flags for prep_swap }
  454.    {d interne Flags für prep_swap }
  455.  
  456.    CREAT_TEMP      = $0080;
  457.    DONT_SWAP_ENV   = $4000;
  458.  
  459.    ERR_COMSPEC     = -7;
  460.    ERR_NOMEM       = -8;
  461.  
  462.    spaces: set of #9..' ' = [#9, ' '];
  463.  
  464. type
  465.    stringptr = ^string;
  466.    stringarray = array [0..10000] of stringptr;
  467.    stringarrptr = ^stringarray;
  468.    bytearray = array [0..30000] of byte;
  469.    bytearrayptr = ^bytearray;
  470.  
  471. var
  472.    envptr: stringarrptr;   { Pointer to the changed environment }
  473.    envcnt: integer;        { Count of environment strings }
  474.    cmdpath: string;
  475.    cmdpars: string;
  476.    drive: string [3];
  477.    dir: string [67];
  478.    name: string [9];
  479.    ext: string [5];
  480.  
  481.  
  482. {$L spawnp}
  483. function do_spawn (swapping: integer;
  484.                    var xeqfn; var cmdtail; envlen: word;
  485.                    var env
  486. {$IFDEF REDIRECT}
  487.                    ;stdin: pstring; stdout: pstring; stderr: pstring
  488. {$ENDIF}
  489.                    ): integer; external;
  490.  
  491. function prep_swap (method: integer; var swapfn): integer; external;
  492.  
  493. { Environment routines }
  494.  
  495. function envcount: integer;
  496.  
  497.    { Returns count of strings in environment. }
  498.  
  499.    var
  500.       cnt: integer;
  501.    begin
  502.    if envptr = nil { If not yet changed }
  503.       then envcount := dos.envcount
  504.       else envcount := envcnt;
  505.    end;
  506.  
  507.  
  508. function envstr (index: integer): string;
  509.  
  510.    { Returns environment string 'index' }
  511.  
  512.    begin
  513.    if envptr = nil { If not yet changed }
  514.       then envstr := dos.envstr (index)
  515.       else if (index <= 0) or (index >= envcnt)
  516.       then envstr := ''
  517.       else if envptr^ [index - 1] = nil
  518.       then envstr := ''
  519.       else envstr := envptr^ [index - 1]^;
  520.    end;
  521.  
  522.  
  523. function name_eq (var n1, n2: string): boolean;
  524.  
  525.    { Compares search string 'n1' with environment string 'n2'.
  526.      Case is insignificant. }
  527.  
  528.    var
  529.       i: integer;
  530.       eq: boolean;
  531.    begin
  532.    i := 1;
  533.    eq := false;
  534.    while (i <= length (n1)) and (i <= length (n2)) and
  535.          (upcase (n1 [i]) = upcase (n2 [i])) do
  536.       i := i + 1;
  537.    name_eq := (i > length (n1)) and (i <= length (n2)) and (n2 [i] = '=');
  538.    end;
  539.  
  540.  
  541. function searchenv (var str: string): integer;
  542.  
  543.    { Search for environment string, returns index in 'envptr' array.
  544.      Assumes 'envptr' is not NIL. }
  545.  
  546.    var
  547.       idx: integer;
  548.       found: boolean;
  549.    begin
  550.    idx := 0;
  551.    found := false;
  552.  
  553.    while (idx < envcnt) and not found do
  554.       begin
  555.       if envptr^ [idx] <> nil
  556.          then found := name_eq (str, envptr^ [idx]^);
  557.       idx := idx + 1;
  558.       end;
  559.    if not found
  560.       then searchenv := -1
  561.       else searchenv := idx - 1;
  562.    end;
  563.  
  564.  
  565. function getenv (envvar: string): string;
  566.  
  567.    { Returns value of environment string specified by name. }
  568.  
  569.    var
  570.       strp: stringptr;
  571.       eq: integer;
  572.    begin
  573.    if envptr = nil { If not yet changed }
  574.       then getenv := dos.getenv (envvar)
  575.       else begin
  576.       eq := searchenv (envvar);
  577.       if eq < 0
  578.          then getenv := ''
  579.          else begin
  580.          strp := envptr^ [eq];
  581.          eq := pos ('=', strp^);
  582.          getenv := copy (strp^, eq + 1, length (strp^) - eq);
  583.          end;
  584.       end;
  585.    end;
  586.  
  587.  
  588. procedure init_envptr;
  589.  
  590.    { Initialise 'envptr' array. Called when 'putenv' is used for the
  591.      first time. Copies all environment strings into heap storage,
  592.      and builds an array of pointers to this strings. }
  593.  
  594.    var
  595.       i: integer;
  596.       str: string [255];
  597.    begin
  598.    envcnt := dos.envcount;
  599.    getmem (envptr, envcnt * sizeof (stringptr));
  600.    if envptr = nil
  601.       then exit;
  602.    for i := 0 to envcnt - 1 do
  603.       begin
  604.       str := dos.envstr (i + 1);
  605.       getmem (envptr^ [i], length (str) + 1);
  606.       if envptr^ [i] <> nil
  607.          then envptr^ [i]^ := str;
  608.       end;
  609.    end;
  610.  
  611.  
  612. procedure putenv (envvar: string);
  613.  
  614.    { Adds the string 'envvar' to the environment, or changes the
  615.      environment string if the name is already present. }
  616.  
  617.    var
  618.       idx, eq: integer;
  619.       help: stringarrptr;
  620.       tmpvar : string;
  621.    begin
  622.    if envptr = nil
  623.       then init_envptr;
  624.    if envptr = nil
  625.       then exit;
  626.  
  627.    eq := pos ('=', envvar);
  628.    if eq = 0
  629.       then exit;
  630.    for idx := 1 to eq do
  631.       envvar [idx] := upcase (envvar [idx]);
  632.    tmpvar := copy (envvar, 1, eq - 1); { Copy the portion up to "=" }
  633.  
  634.    idx := searchenv (tmpvar);
  635.    if idx >= 0
  636.       then begin
  637.       freemem (envptr^ [idx], length (envptr^ [idx]^) + 1);
  638.  
  639.       if eq >= length (envvar)
  640.          then envptr^ [idx] := nil
  641.          else begin
  642.          getmem (envptr^ [idx], length (envvar) + 1);
  643.          if envptr^ [idx] <> nil
  644.             then envptr^ [idx]^ := envvar;
  645.          end;
  646.       end
  647.       else if eq < length (envvar)
  648.       then begin
  649.       getmem (help, (envcnt + 1) * sizeof (stringptr));
  650.       if help = nil
  651.          then exit;
  652.       move (envptr^, help^, envcnt * sizeof (stringptr));
  653.       freemem (envptr, envcnt * sizeof (stringptr));
  654.       envptr := help;
  655.       getmem (envptr^ [envcnt], length (envvar) + 1);
  656.       if envptr^ [envcnt] <> nil
  657.          then envptr^ [envcnt]^ := envvar;
  658.       envcnt := envcnt + 1;
  659.       end;
  660.    end;
  661.  
  662.  
  663.  
  664. { Routines to search for files }
  665.  
  666. function tryext (var fn: string): integer;
  667.  
  668.    { Try '.COM', '.EXE', and '.BAT' on current filename, modify filename if found. }
  669.  
  670.    var
  671.       nfn: filename;
  672.       ok: boolean;
  673.    begin
  674.    tryext := 1;
  675.    nfn := fn + '.COM';
  676.    ok := exists (nfn);
  677.    if not ok
  678.       then begin
  679.       nfn := fn + '.EXE';
  680.       ok := exists (nfn);
  681.       end;
  682.    if not ok
  683.       then begin
  684.       tryext := 2;
  685.       nfn := fn + '.BAT';
  686.       ok := exists (nfn);
  687.       end;
  688.    if not ok
  689.       then tryext := 0
  690.       else fn := nfn;
  691.    end;
  692.  
  693.  
  694. function findfile (var fn: string): integer;
  695.  
  696.    { Try to find the file 'fn' in the current path. Modifies the filename
  697.      accordingly. }
  698.  
  699.    var
  700.       path: string;
  701.       i, j: integer;
  702.       hasext, found, check: integer;
  703.    begin
  704.    if fn = ''
  705.       then begin
  706.       if cmdpath = ''
  707.          then findfile := ERR_COMSPEC
  708.          else findfile := 3;
  709.       exit;
  710.       end;
  711.  
  712.    check := checkpath (fn, INF_NODIR, drive, dir, name, ext, fn);
  713.    if check < 0
  714.       then begin
  715.       findfile := check;
  716.       exit;
  717.       end;
  718.  
  719.    if ((check and HAS_WILD) <> 0) or ((check and HAS_FNAME) = 0)
  720.       then begin
  721.       findfile := ERR_FNAME;
  722.       exit;
  723.       end;
  724.  
  725.    if (check and HAS_EXT) <> 0
  726.       then begin
  727.       for i := 1 to length (ext) do
  728.          ext [i] := upcase (ext [i]);
  729.       if ext = '.BAT'
  730.          then hasext := 2
  731.          else hasext := 1;
  732.       end
  733.       else hasext := 0;
  734.  
  735.    if hasext <> 0
  736.       then begin
  737.       if (check and FILE_EXISTS) <> 0
  738.          then found := hasext
  739.          else found := 0;
  740.       end
  741.       else found := tryext (fn);
  742.  
  743.    if (found <> 0) or ((check and (HAS_PATH or HAS_DRIVE)) <> 0)
  744.       then begin
  745.       findfile := found;
  746.       exit;
  747.       end;
  748.  
  749.    path := getenv ('PATH');
  750.    i := 1;
  751.    while (found = 0) and (i <= length (path)) do
  752.       begin
  753.       j := 0;
  754.       while (path [i] <> ';') and (i <= length (path)) do
  755.          begin
  756.          j := j + 1;
  757.          fn [j] := path [i];
  758.          i := i + 1;
  759.          end;
  760.       i := i + 1;
  761.       if (j > 0)
  762.          then begin
  763.          if not (fn [j] in ['\', '/'])
  764.             then begin
  765.             j := j + 1;
  766.             fn [j] := '\';
  767.             end;
  768.          fn [0] := chr (j);
  769.          fn := fn + name + ext;
  770.          check := checkpath (fn, INF_NODIR, drive, dir, name, ext, fn);
  771.          if hasext <> 0
  772.             then begin
  773.             if (check and FILE_EXISTS) <> 0
  774.                then found := hasext
  775.                else found := 0;
  776.             end
  777.             else found := tryext (fn);
  778.          end;
  779.       end;
  780.    findfile := found;
  781.    end; { findfile }
  782.  
  783.  
  784. {>e 
  785.    Get name and path of the command processor via the COMSPEC
  786.    environmnt variable. Any parameters after the program name
  787.    are copied and inserted into the command line.
  788. <}
  789. {>d
  790.    Namen und Pfad des Kommandoprozessors über die COMSPEC-Umgebungs-
  791.    Variable bestimmen. Parameter nach dem Programmnamen werden kopiert
  792.    und in die Kommandozeile eingefügt.
  793. <}
  794.  
  795. procedure getcmdpath;
  796.    var
  797.       i, found: integer;
  798.    begin
  799.    if length (cmdpath) > 0
  800.       then exit;
  801.    cmdpath := getenv ('COMSPEC');
  802.    cmdpars := '';
  803.    found := 0;
  804.  
  805.    if cmdpath <> ''
  806.       then begin
  807.       i := 1;
  808.       while (i <= length (cmdpath)) and (cmdpath [i] in spaces) do
  809.          inc (i);
  810.       if i > 1
  811.          then begin
  812.          cmdpath := copy (cmdpath, i, 255);
  813.          i := 1;
  814.          end;
  815.  
  816.       i := pos (';,=+/"[]|<> '#9, cmdpath);
  817.       if i <> 0
  818.          then begin
  819.          cmdpars := copy (cmdpath, i, 128);
  820.          cmdpath [0] := chr (i - 1);
  821.          i := 1;
  822.          while (i <= length (cmdpars)) and (cmdpars [i] in spaces) do
  823.             inc (i);
  824.          if i > 1
  825.             then cmdpars := copy (cmdpars, i, 128);
  826.          if cmdpars <> ''
  827.             then cmdpars := cmdpars + ' ';
  828.          end;
  829.       found := findfile (cmdpath);
  830.       end;
  831.  
  832.    if found = 0
  833.       then begin
  834.       cmdpath := 'COMMAND.COM';
  835.       cmdpars := '';
  836.       found := findfile (cmdpath);
  837.       if found = 0
  838.          then cmdpath := '';
  839.       end;
  840.    end;
  841.  
  842.  
  843. function tempdir (var outfn: filename): boolean;
  844.  
  845.    { Set temporary file path.
  846.      Read "TMP/TEMP" environment. If empty or invalid, clear path.
  847.      If TEMP is drive or drive+backslash only, return TEMP.
  848.      Otherwise check if given path is a valid directory.
  849.    }
  850.    var
  851.       stmp: array [0..3] of filename;
  852.       i, res: integer;
  853.  
  854.    begin
  855.    stmp [0] := getenv ('TMP');
  856.    stmp [1] := getenv ('TEMP');
  857.    stmp [2] := '.\';
  858.    stmp [3] := '\';
  859.  
  860.    for i := 0 to 3 do
  861.       if length (stmp [i]) <> 0
  862.          then begin
  863.          outfn := stmp [i];
  864.          res := checkpath (outfn, 0, drive, dir, name, ext, outfn);
  865.          if (res > 0) and ((res and IS_DIR) <> 0) and ((res and IS_READ_ONLY) = 0)
  866.             then begin
  867.             tempdir := true;
  868.             exit;
  869.             end;
  870.          end;
  871.    tempdir := false;
  872.    end;
  873.  
  874.  
  875. {$IFDEF REDIRECT}
  876.  
  877. function parse_redirect (var par: string; idx: integer;
  878.                          var stdin, stdout, stderr: pstring): boolean;
  879.    var
  880.       ch: char;
  881.       fnp: pstring;
  882.       fn: string;
  883.       app, i, fne: integer;
  884.  
  885.    begin
  886.    i := idx;
  887.    par [length (par) + 1] := #0;
  888.  
  889.    repeat
  890.       app := 0;
  891.       ch := par [i];
  892.       i := i + 1;
  893.       if ch <> '<'
  894.          then begin
  895.          if par [i] = '&'
  896.             then begin
  897.             ch := '&';
  898.             inc (i);
  899.             end;
  900.          if par [i] = '>'
  901.             then begin
  902.             app := 1;
  903.             inc (i);
  904.             end;
  905.          end;
  906.  
  907.       while (i <= length (par)) and (par [i] in spaces) do
  908.          inc (i);
  909.       fn := copy (par, i, 255);
  910.       fne := pos (';,=+/"[]|<> '#9, fn);
  911.       if fne = 0
  912.          then fne := length (fn) + 1;
  913.       i := i + fne - 1;
  914.       fn [0] := chr (fne - 1);
  915.       if (fne = 0) or (length (fn) = 0)
  916.          then begin
  917.          parse_redirect := false;
  918.          exit;
  919.          end;
  920.       
  921.       getmem (fnp, length (fn) + app + 2);
  922.       if fnp = NIL
  923.          then begin
  924.          parse_redirect := false;
  925.          exit;
  926.          end;
  927.       if app <> 0
  928.          then fnp^ := '>' + fn
  929.          else fnp^ := fn;
  930.       fnp^ [length (fnp^) + 1] := #0;
  931.  
  932.       case ch of
  933.          '<':  if stdin <> NIL
  934.                   then begin
  935.                   parse_redirect := false;
  936.                   exit;
  937.                   end
  938.                else stdin := fnp;
  939.  
  940.          '>':  if stdout <> NIL
  941.                   then begin
  942.                   parse_redirect := false;
  943.                   exit;
  944.                   end
  945.                else stdout := fnp;
  946.  
  947.          '&':  if stderr <> NIL
  948.                   then begin
  949.                   parse_redirect := false;
  950.                   exit;
  951.                   end
  952.                else stderr := fnp;
  953.          end;
  954.  
  955.       while (i <= length (par)) and (par [i] in spaces) do
  956.          inc (i);
  957.  
  958.    until (i > length (par)) or (par [i] <> '>') and (par [i] <> '<');
  959.  
  960.    par [idx] := #0;
  961.    par [0] := chr (idx - 1);
  962.    parse_redirect := true;
  963.    end;
  964.  
  965. {$ENDIF}
  966.  
  967.  
  968. function do_exec (xfn: string; pars: string; spawn: integer;
  969.                   needed: word; newenv: boolean): integer;
  970.    label
  971.       exit;
  972.    var
  973.       cmdbat: integer;
  974.       swapfn: filename;
  975.       avail: word;
  976.       regs: registers;
  977.       envlen, einx: word;
  978.       idx, len, rc: integer;
  979.       envp: bytearrayptr;
  980.       swapping: integer;
  981. {$IFDEF REDIRECT}
  982.       stdin, stdout, stderr: pstring;
  983. {$ENDIF}
  984.    begin
  985. {$IFDEF REDIRECT}
  986.    stdin := NIL; stdout := NIL; stderr := NIL;
  987. {$ENDIF}
  988.  
  989.    getcmdpath;
  990.    envlen := 0;
  991.  
  992.    { First, check if the file to execute exists. }
  993.  
  994.    cmdbat := findfile (xfn);
  995.    if cmdbat <= 0
  996.       then begin
  997.       do_exec := RC_NOFILE or -cmdbat;
  998.       goto exit;
  999.       end;
  1000.  
  1001.    if cmdbat > 1   { COMMAND.COM or Batch file }
  1002.       then begin
  1003.       if length (cmdpath) = 0
  1004.          then begin
  1005.          do_exec := RC_NOFILE or -ERR_COMSPEC;
  1006.          goto exit;
  1007.          end;
  1008.  
  1009.       if cmdbat = 2
  1010.          then pars := cmdpars + '/c ' + xfn + ' ' + pars
  1011.          else pars := cmdpars + pars;
  1012.       xfn := cmdpath;
  1013.       end;
  1014.  
  1015. {$IFDEF REDIRECT}
  1016.    idx := pos ('<', pars);
  1017.    len := pos ('>', pars);
  1018.    if len > idx
  1019.       then idx := len;
  1020.    if idx > 0
  1021.       then if not parse_redirect (pars, idx, stdin, stdout, stderr)
  1022.          then begin
  1023.          do_exec := RC_REDIRERR;
  1024.          goto exit;
  1025.          end;
  1026. {$ENDIF}
  1027.  
  1028.    { Now create a copy of the environment if the user wants it, and
  1029.      if the environment has been changed. }
  1030.  
  1031.    if newenv and (envptr <> nil)
  1032.       then begin
  1033.       for idx := 0 to envcnt - 1 do
  1034.          envlen := envlen + length (envptr^ [idx]^) + 1;
  1035.       if envlen > 0
  1036.          then begin
  1037.          envlen := envlen + 1;
  1038.          getmem (envp, envlen);
  1039.          if envp = nil
  1040.             then begin
  1041.             do_exec := RC_ENVERR;
  1042.             goto exit;
  1043.             end;
  1044.          einx := 0;
  1045.          for idx := 0 to envcnt - 1 do
  1046.             begin
  1047.             len := length (envptr^ [idx]^);
  1048.             move (envptr^ [idx]^ [1], envp^ [einx], len);
  1049.             envp^ [einx + len] := 0;
  1050.             einx := einx + len + 1;
  1051.             end;
  1052.          envp^ [einx] := 0;
  1053.          end;
  1054.       end;
  1055.  
  1056.    if spawn = 0
  1057.       then swapping := -1
  1058.       else begin
  1059.  
  1060.       { Determine amount of free memory }
  1061.       with regs do
  1062.          begin
  1063.          ax := $4800;
  1064.          bx := $ffff;
  1065.          msdos (regs);
  1066.          avail := regs.bx;
  1067.          end;
  1068.  
  1069.       { No swapping if available memory > needed }
  1070.  
  1071.       if needed < avail
  1072.          then swapping := 0
  1073.          else begin
  1074.  
  1075.          { Swapping necessary, use 'TMP' or 'TEMP' environment variable
  1076.            to determine swap file path if defined. }
  1077.  
  1078.          swapping := spawn;
  1079.          if (spawn and USE_FILE) <> 0
  1080.             then begin
  1081.             if not tempdir (swapfn)
  1082.                then begin
  1083.                spawn := spawn xor USE_FILE;
  1084.                swapping := spawn;
  1085.                end
  1086.                else begin
  1087.                if (dosversion and $ff) >= 3
  1088.                   then swapping := swapping or CREAT_TEMP
  1089.                   else begin
  1090.                   swapfn := swapfn + swap_filename;
  1091.                   len := length (swapfn);
  1092.                   while exists (swapfn) do
  1093.                      begin
  1094.                       if (swapfn [len] >= 'Z')
  1095.                         then len := len - 1;
  1096.                       if (swapfn [len] = '.')
  1097.                         then len := len - 1;
  1098.                       swapfn [len] := succ (swapfn [len]);
  1099.                       end;
  1100.                   end;
  1101.                swapfn [length (swapfn) + 1] := #0;
  1102.                end;
  1103.             end;
  1104.          end;
  1105.       end;
  1106.  
  1107.    { All set up, ready to go. }
  1108.  
  1109.    if swapping > 0
  1110.       then begin
  1111.       if envlen = 0
  1112.          then swapping := swapping or DONT_SWAP_ENV;
  1113.  
  1114.       rc := prep_swap (swapping, swapfn);
  1115.       if rc < 0
  1116.          then begin
  1117.          do_exec := RC_PREPERR or -rc;
  1118.          goto exit;
  1119.          end;
  1120.       end;
  1121.  
  1122.    xfn [length (xfn) + 1] := #0;
  1123.    pars [length (pars) + 1] := #0;
  1124.  
  1125.    if @spawn_check <> NIL
  1126.       then begin
  1127.       rc := spawn_check (cmdbat, swapping, xfn, pars);
  1128.       if rc <> 0
  1129.          then begin
  1130.          do_exec := rc;
  1131.          goto exit;
  1132.          end;
  1133.       end;
  1134.  
  1135.    swapvectors;
  1136. {$IFDEF REDIRECT}
  1137.    do_exec := do_spawn (swapping, xfn, pars, envlen, envp^, stdin, stdout, stderr);
  1138. {$ELSE}
  1139.    do_exec := do_spawn (swapping, xfn, pars, envlen, envp^);
  1140. {$ENDIF}
  1141.    swapvectors;
  1142.  
  1143.    { Free the environment buffer if it was allocated. }
  1144.  
  1145. exit:
  1146.    if envlen > 0
  1147.       then freemem (envp, envlen);
  1148. {$IFDEF REDIRECT}
  1149.    if stdin <> NIL
  1150.       then freemem (stdin, length (stdin^) + 2);
  1151.    if stdout <> NIL
  1152.       then freemem (stdout, length (stdout^) + 2);
  1153.    if stderr <> NIL
  1154.       then freemem (stderr, length (stderr^) + 2);
  1155. {$ENDIF}
  1156.    end;
  1157.  
  1158.  
  1159. { Initialisation for environment processing }
  1160.  
  1161. Begin
  1162. envptr := nil;
  1163. envcnt := 0;
  1164. cmdpath := '';
  1165. @spawn_check := nil;
  1166. End.
  1167.  
  1168. 
  1169.